import { Injectable } from '@nestjs/common';
import { Dept } from '@prisma/client';
import { PrismaService } from 'src/prisma/prisma.service';
import { DeptList } from 'src/type/dept';
import { isEmpty, transformToOption } from 'src/utils';
import { CreateDeptDto } from './dto/create-dept.dto';
import { FindAllDeptDto } from './dto/find-all-dept.dto';
import { UpdateDeptDto } from './dto/update-dept.dto';

@Injectable()
export class DeptService {
  constructor(private prisma: PrismaService) {}
  /**
   * 函数“findAll”根据提供的查询参数检索部门列表，并根据需要将它们组织成树结构。
   * @param {FindAllDeptDto} [query] - `findAll` 方法用于根据提供的查询参数检索部门列表。`query` 参数是 `FindAllDeptDto`
   * 类型的可选对象，可能包含以下属性：
   * @returns `findAll` 方法返回 `DeptList` 对象数组，如果 `query` 参数不为空，则返回 `dept` 数组，不形成树结构；如果 `query`
   * 参数为空，则递归生成部门树结构，并只返回 `parentId` 为 0 的部门。
   */
  async findAll(query?: FindAllDeptDto): Promise<DeptList[]> {
    const rawDept: Dept[] = await this.prisma.dept.findMany({
      where: {
        name: {
          contains: query?.keywords,
        },
        status: query?.status,
      },
    });

    // 初始化 children 属性
    const dept = rawDept.map((item) => {
      return {
        id: item.id,
        name: item.name,
        parentId: item.parentId,
        createTime: item.createTime,
        updateTime: item.updateTime,
        sort: item.sort,
        status: item.status,
        children: [],
      };
    });
    // 如果查询参数不为空，就不返回树形结构
    if (!isEmpty(query)) {
      return dept;
    }

    // 递归生成部门树
    dept.forEach((item) => {
      if (item.parentId) {
        const parent = dept.find((i) => i.id === item.parentId);
        if (parent) {
          parent.children.push(item);
        }
      }
    });

    // 只返回 parentId 为 0 的部门
    return dept.filter((item) => item.parentId === 0);
  }

  /**
   * `remove` 函数使用 Prisma 根据提供的 ID 删除部门记录。
   * @param {number} id - `remove` 函数接受一个数字类型的 `id` 参数。此 `id` 用于标识需要从数据库中删除的部门。然后，该函数使用此 `id` 使用 Prisma
   * 客户端的 `dept.delete` 从数据库中删除相应的部门条目
   * @returns `remove` 函数返回使用 Prisma 客户端的 `dept.delete` 方法删除具有指定 `id` 的部门的结果。
   */
  remove(id: number) {
    return this.prisma.dept.delete({
      where: {
        id,
      },
    });
  }

  /**
   * 异步删除多个部门及其所有子部门。
   * @param ids 需要删除的部门ID数组。
   * @returns 删除操作的影响行数。
   */
  async removeMany(ids: number[]) {
    // 递归函数，用于获取指定部门ID的所有子部门ID
    const getAllChildDeptIds = async (parentId: number): Promise<number[]> => {
      const childDepts = await this.prisma.dept.findMany({
        where: {
          parentId,
        },
      });

      const allChildIds: number[] = [];
      for (const dept of childDepts) {
        const childIds = await getAllChildDeptIds(dept.id);
        allChildIds.push(dept.id, ...childIds);
      }

      return allChildIds;
    };

    const allDeptIds: number[] = [];
    // 遍历每个要删除的部门ID，获取其所有子部门ID
    for (const id of ids) {
      const childIds = await getAllChildDeptIds(id);
      allDeptIds.push(id, ...childIds);
    }

    // 一次性删除所有部门
    return this.prisma.dept.deleteMany({
      where: {
        id: {
          in: allDeptIds,
        },
      },
    });
  }

  /**
   * `findOptions` 函数异步检索所有部门并使用其 ID 将其转换为选项。
   * @returns `findOptions` 函数返回调用 `transformToOption` 函数的结果，其中 `dept` 数组作为第一个参数，字符串 `'id'` 作为第二个参数。
   */
  async findOptions() {
    const dept = await this.findAll();
    return transformToOption(dept);
  }

  /**
   * 此 TypeScript 函数使用 Prisma 通过 ID 查找部门。
   * @param {number} id - `findOne` 函数采用数字类型的 `id` 参数。此函数使用 `prisma` 客户端根据提供的 `id` 查找唯一的部门记录。
   * @returns `findOne` 方法根据提供的 `id` 从数据库返回单个部门对象。它使用 Prisma 客户端查询数据库并查找 `id` 与输入参数匹配的唯一部门记录。
   */
  findOne(id: number) {
    return this.prisma.dept.findUnique({ where: { id } });
  }

  /**
   * 函数“findForm”根据提供的ID检索特定部门表格。
   * @param {number} id - `id` 参数是要检索的表单的唯一标识符。它用于查询数据库并查找具有相应 ID 的表单。
   * @returns `findForm` 函数从数据库的 `dept` 表中返回具有指定属性（`id`、`name`、`parentId`、`sort`、`status`）的表单对象，其中 `id`
   * 与输入参数匹配。
   */
  findForm(id: number) {
    return this.prisma.dept.findUnique({ where: { id }, select: { id: true, name: true, parentId: true, sort: true, status: true } });
  }

  /**
   * 更新函数以 id 和数据对象作为参数，使用 Prisma 更新数据库中的部门记录。
   * @param {number} id - `id` 参数是一个数字，代表您想要在数据库中更新的部门的唯一标识符。
   * @param {UpdateDeptDto} data - `update` 函数中的 `data` 参数可能包含您想要更新的部门信息。它属于 `UpdateDeptDto`
   * 类型，这表明它是专门为更新部门信息而设计的数据传输对象。此对象可能包含部门名称、描述等属性
   * @returns `update` 方法返回使用 Prisma Client 更新数据库中部门记录的结果。
   */
  update(id: number, data: UpdateDeptDto) {
    return this.prisma.dept.update({
      where: {
        id,
      },
      data,
    });
  }

  /**
   * 此 TypeScript 函数根据部门名称检查部门是否已存在，如果不存在则创建一个新部门。
   * @param {CreateDeptDto} body - 您提供的“create”函数是一个异步函数，它根据提供的“CreateDeptDto”对象创建一个新的部门。
   * @returns 如果该部门尚不存在，则将创建并返回一个新的部门。
   */
  async create(body: CreateDeptDto) {
    // 检查部门是否已经存在
    const existingDept = await this.prisma.dept.findFirst({
      where: { name: body.name }, // 假设我们是根据部门名称检查的
    });

    if (existingDept) {
      throw new Error('部门已经存在'); // 如果部门已经存在，抛出一个错误
    }

    // 如果部门不存在，那么创建新部门
    return this.prisma.dept.create({
      data: body,
    });
  }
}
